/*
 * Controller for the Ledger Document.
 *
 * Author: Kris Younger, NeXT Systems Engineering
 * You may freely copy, distribute and reuse the code in this example.  
 * NeXT disclaims any warranty of any kind, expressed or implied, as to 
 * its fitness for any particular use.
 */

#import "LedgerController.h"
#import "Transaction.h"
#import <dbkit/dbkit.h>
#import "JFTableVectorConfiguration.h"
#import "JFTableViewLoader.h"
#import "KAYEditableFormatter.h"
#define XOFFSET 30.0 	// Offset of subsequent windows
#define YOFFSET -30.0

@implementation LedgerController

- append:sender
{
    id                  newXaction;
    int                 handle;

    newXaction = [[Transaction alloc] init];
    [accountStore startTransaction];
    handle = [account addRecord:newXaction];
    [accountStore commitTransaction];
    if (sender != myFormatter) [self ping:sender];
    return self;
}

- appendThis:anObj
{
    int                 handle;

    [accountStore startTransaction];
    handle = [account addRecord:anObj];
    [accountStore commitTransaction];
    return self;
}

- commit:sender
{
    int                 r;
    id                  o;

    r = [tableView selectedRowAfter:DB_NoIndex];
    if (r != DB_NoIndex) {
	o = (Transaction *)[postingList objectAt:r];
	[accountStore startTransaction];
	[account replaceRecord:[o handle] with:o];
	[accountStore commitTransaction];
	if (r == ([postingList count] - 1)) {
	    [self doBalance];
	    if (sender != myFormatter) [self ping:self];
	} else
	    [self doFullerBalance:self];
    };
    return self;
}

- copy:sender
{
    return self;
}

- cut:sender
{
    return self;
}

- paste:sender
{
    return self;
}

- selectAll:sender
{
    return self;
}

- initWith:aRecordManager named:(const char *)aName
{
    char                path[MAXPATHLEN + 1];
    int                 i;

    if ([[NXBundle mainBundle] getPath:path forResource:"LedgerController" ofType:"nib"]) {
	window = [NXApp loadNibFile:path owner:self];
    /*
     * Put the window offset from the previous document window... If no
     * previous window exists, or the main window is undetermined, then 
     */
	if ([[[NXApp mainWindow] delegate] respondsTo:@selector(isLedger)] == YES) {
	    NXRect              winFrame, winLoc;

	    [[NXApp mainWindow] getFrame:&winFrame];
	    [[window class] getContentRect:&winLoc 
		forFrameRect:&winFrame style:[window style]];
	    [window moveTo:NX_X(&winLoc) + XOFFSET:NX_Y(&winLoc) + YOFFSET];
	}
/* the tvLoader is an object which takes a list of objects and a list of configuration objects (see below) which work to load the tableview when it requests it. */
	tvLoader = [[JFTableViewLoader alloc] init];
	account = aRecordManager;
	accountName = aName;
	[account getBlock:&i andStore:&accountStore];
	[tvLoader setTableView:tableView];
	[[tableView setGridVisible:YES] setDelegate:self];
	[[tableView window] setTitle:accountName];
#ifdef NEVEREVER
		/* useful in making window positions sticky on the
				 * desktop. */
	[[tableView window] setFrameAutosaveName:accountName];
	[[tableView window] setFrameUsingName:accountName];
#endif
	myFormatter = [[KAYEditableFormatter alloc] init];
	[myFormatter setController:self];
	[[tableView window] disableFlushWindow];
	[self buildConfigurationList];
/* go check out the method, it loads a list full of "bindings" binding a tableview column to an ivar inside of the Transaction object. essentially, letting us use a list of objects instead of a recordlist. */
	[tvLoader setConfigurationList:configList];
	[myFormatter setColumnLimit:[configList count] - 1];
	[[tableView window] reenableFlushWindow];
	[[tableView window] flushWindowIfNeeded];
	for (i = 0; i < [configList count]; i++) {
	    [[tableView columnAt:i] setFormatter:myFormatter];
	};
	doFullBalance = NO;
    } else {
	NXRunAlertPanel("Oops!", "No LedgerController nib file", "OK", NULL, NULL);
	return nil;
    }
    return self;
}

- findPostings
{
    int			handle;
    id			config;
    IXPostingCursor	*cursor;
    BOOL		isValid;

/* since the Transaction objects are serialized by creation (handled in the main delegate), this method uses the attribute "tSerial" to get a sorted-ascending ordering. The postingList is what we use load the tableview */

    config = [[tableView columnAt:0] identifier];
    cursor = [account cursorForAttributeNamed:
	[[configList objectAt:(int)config] ivarName]];
    if (cursor == nil)
	cursor = [account cursorForAttributeNamed:"tSerial"];

    postingList = [postingList free];
    postingList = [[IXPostingList alloc] initWithSource:account];
    for (isValid = [cursor setFirst]; isValid; isValid = [cursor setNext])
	for (handle = [cursor setFirstHandle]; handle; handle = [cursor setNextHandle])
	    [postingList addHandle:handle withWeight:0];

    [cursor free];
    return postingList;
}

- makeKey:sender
{
    [self doBalance];
    [self ping:self];
    return self;
}

- (const char *)accountName
{
    return accountName;
}

- doBalance
{
    double              cBalance = 0.0;
    char                buf[256];
    int                 i, k, h, m;
    id                  obj;

    postingList = [self findPostings];
    k = [postingList count];
    [accountStore startTransaction];
    if (((k - 2) <= 0) || (doFullBalance == YES)) {
	m = 0;
	cBalance = 0.0;
    } else {
	m = (k - 2);
	h = [postingList handleOfObjectAt:(m - 1)];
	obj = [account readRecord:h fromZone:[self zone]];
	strcpy(buf,[obj tBalance]);
	cBalance += atof(buf);
    };
    for (i = m; i < k; i++) {
	h = [postingList handleOfObjectAt:i];
	obj = [account readRecord:h fromZone:[self zone]];
	cBalance += [obj tAmount];
	sprintf(buf, "%.2f", cBalance);
	[obj setTBalance:buf];
	[account replaceRecord:[obj handle] with:obj];
	[obj free];
    }
    [accountStore commitTransaction];
    sprintf(buf, "%.2f", cBalance);
    [balanceTF setStringValue:buf];
    return self;
}

- doFullerBalance:sender
{
    doFullBalance = YES;
    [self doBalance];
    [self ping:self];
    doFullBalance = NO;

    return self;
}

- ping:sender
{

    [window makeKeyAndOrderFront:self];
    [[tableView window] disableFlushWindow];	/* DBTV ignores autodisplay */
    postingList = [self findPostings];
    [tvLoader setDataList:postingList];
    [tableView scrollRowToVisible:[tableView rowCount] - 1];
    [tableView editFieldAt:[tableView rowCount] - 1:0];
    [[tableView window] reenableFlushWindow];
    [[tableView window] flushWindowIfNeeded];
    return self;
}

- void:sender
{
    int                 r, h;
    id                  o, p;

    r = [tableView selectedRowAfter:DB_NoIndex];
    if (r != DB_NoIndex) {
	o = (Transaction *)[postingList objectAt:r];
	p = [[Transaction alloc] init];
	[accountStore startTransaction];
	h = [account addRecord:p];
	[p becomeVoided:o];
	[account replaceRecord:[o handle] with:o];
	[account replaceRecord:[p handle] with:p];
	[accountStore commitTransaction];
	[self doBalance];
	[self ping:self];
    };
    return self;
}

- closeAccount:sender
{
    [window performClose:self];
    return self;
}

- windowWillClose:sender
{
    return self;
}

- tableView:sender movedColumnFrom:(unsigned int)old to:(unsigned int)new
{
    if (new == 0) [self ping:self];
    return self;
}

- buildConfigurationList
{
    configList = [[List alloc] init];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"Date"
			   ivarName:"tDate"]];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"REF"
			   ivarName:"tNumber"]];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"Description"
			   ivarName:"tMemo"]];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"Debit"
			   ivarName:"tDebit"]];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"Credit"
			   ivarName:"tCredit"]];
    [configList addObject:[[JFTableVectorConfiguration alloc]
			   initDataClassName:"Transaction"
			   titleName:"Balance"
			   ivarName:"tBalance"]];
    return self;
}

@end
